/*-------------------------------------------------------------------------
 *
 * ts_utils.h
 *	  Tsearch 的辅助工具
 *
 * Copyright (c) 1998-2022, PostgreSQL Global Development Group
 *
 * src/include/tsearch/ts_utils.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef _PG_TS_UTILS_H_
#define _PG_TS_UTILS_H_

#include "nodes/pg_list.h"
#include "tsearch/ts_public.h"
#include "tsearch/ts_type.h"

/*
 * tsvector 和 tsquery 的通用解析定义
 */

/* tsvector 解析器支持。 */

struct TSVectorParseStateData;	/* tsvector_parser.c 中的不透明结构 */
typedef struct TSVectorParseStateData *TSVectorParseState;

#define P_TSV_OPR_IS_DELIM	(1 << 0)
#define P_TSV_IS_TSQUERY	(1 << 1)
#define P_TSV_IS_WEB		(1 << 2)

extern TSVectorParseState init_tsvector_parser(char *input, int flags);
extern void reset_tsvector_parser(TSVectorParseState state, char *input);
extern bool gettoken_tsvector(TSVectorParseState state,
							  char **token, int *len,
							  WordEntryPos **pos, int *poslen,
							  char **endptr);
extern void close_tsvector_parser(TSVectorParseState state);

/* 短语操作符以 '<' 开始 */
#define ISOPERATOR(x) \
	( pg_mblen(x) == 1 && ( *(x) == '!' ||	\
							*(x) == '&' ||	\
							*(x) == '|' ||	\
							*(x) == '(' ||	\
							*(x) == ')' ||	\
							*(x) == '<'		\
						  ) )

/* parse_tsquery */

struct TSQueryParserStateData;	/* 在 backend/utils/adt/tsquery.c 中私有 */
typedef struct TSQueryParserStateData *TSQueryParserState;

typedef void (*PushFunction) (Datum opaque, TSQueryParserState state,
							  char *token, int tokenlen,
							  int16 tokenweights,	/* bitmap 如 QueryOperand 结构所述 */
							  bool prefix);

#define P_TSQ_PLAIN		(1 << 0)
#define P_TSQ_WEB		(1 << 1)

extern TSQuery parse_tsquery(char *buf,
							 PushFunction pushval,
							 Datum opaque,
							 int flags);

/* PushFunction 实现使用的函数 */
extern void pushValue(TSQueryParserState state,
					  char *strval, int lenval, int16 weight, bool prefix);
extern void pushStop(TSQueryParserState state);
extern void pushOperator(TSQueryParserState state, int8 oper, int16 distance);

/*
 * 解析普通文本和词汇化单词
 */
typedef struct
{
	uint16		len;
	uint16		nvariant;
	union
	{
		uint16		pos;

		/*
		 * 当使用 apos 数组时，apos[0] 是数组中的元素数量（不包括 apos[0]），
		 * alen 是数组的分配大小。
		 */
		uint16	   *apos;
	}			pos;
	uint16		flags;			/* 当前，仅支持 TSL_PREFIX */
	char	   *word;
	uint32		alen;
} ParsedWord;

typedef struct
{
	ParsedWord *words;
	int32		lenwords;
	int32		curwords;
	int32		pos;
} ParsedText;

extern void parsetext(Oid cfgId, ParsedText *prs, char *buf, int32 buflen);

/*
 * headline 框架，通用流程生成：
 *	1 用 hlparsetext 解析文本
 *	2 特定解析器的函数查找部分
 *	3 generateHeadline 生成结果文本
 */

extern void hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query,
						char *buf, int32 buflen);
extern text *generateHeadline(HeadlineParsedText *prs);

/*
 * TSQuery 执行支持
 *
 * TS_execute() 执行单个 tsquery 针对可以以各种形式表示的数据。
 * TSExecuteCallback 回调函数用于检查给定的原始 tsquery 值是否在数据中匹配。
 */

/* TS_execute 要求三元逻辑来处理与短语匹配的 NOT */
typedef enum
{
	TS_NO,						/* 确定没有匹配 */
	TS_YES,						/* 确实匹配 */
	TS_MAYBE					/* 由于缺少位置数据无法验证匹配 */
} TSTernaryValue;

/*
 * 如果需要词素位置数据（因为 tsquery 中的短语匹配操作符），
 * struct ExecPhraseData 将传递给 TSExecuteCallback 函数。
 * 当它返回 TS_YES（成功）时，回调应填充位置数据。
 * 如果无法返回位置数据，它应保持 "data" 不变并返回 TS_MAYBE。
 * TS_execute() 的调用者必须安排稍后在数据可用时重新检查位置数据。
 *
 * 报告的词素位置必须是排序且唯一的。调用者只能参考 pos 数组的位置位，
 * 即 WEP_GETPOS(data->pos[i])。这允许返回的 "pos" 直接指向 tsvector 值的
 * WordEntryPos 部分。如果 "allocated" 为真，则 pos 数组为 palloc'd 工作区，
 * 调用者可以在完成时释放它。
 *
 * "negate" 意味着 pos 数组包含查询不匹配的位置，而不是匹配的位置。
 * 当匹配宽度超过一个词素时，"width" 为正数。通常，TSExecuteCallback 函数
 * 不需要触碰这两个字段；它们用于 TS_execute 中的短语搜索处理。
 *
 * ExecPhraseData 结构的所有字段最初由调用者置为零。
 */
typedef struct ExecPhraseData
{
	int			npos;			/* 报告的位置数量 */
	bool		allocated;		/* pos 指向分配的数据？ */
	bool		negate;			/* 位置是查询未匹配的位置 */
	WordEntryPos *pos;			/* 排序的、非重复的词素位置 */
	int			width;			/* 匹配中的词素宽度，减去 1 */
} ExecPhraseData;

/*
 * TSQuery 词素检查函数的签名
 *
 * arg：从 TS_execute 调用者传递的透明值
 * val：要测试存在的词素
 * data：填充词素位置；如果不需要位置数据则为 NULL
 *
 * 如果数据中存在词素，则返回 TS_YES；如果可能存在则返回
 * TS_MAYBE；如果肯定不存在，则返回 TS_NO。如果 data 不是 NULL，
 * 则在可用的情况下必须填充词素位置。如果无法获得位置数据，
 * 则将 *data 保持为零并返回 TS_MAYBE，绝不要返回 TS_YES。
 */
typedef TSTernaryValue (*TSExecuteCallback) (void *arg, QueryOperand *val,
											 ExecPhraseData *data);

/*
 * TS_execute 的标志位
 */
#define TS_EXEC_EMPTY			(0x00)
/*
 * 如果设置了 TS_EXEC_SKIP_NOT，则 NOT 子表达式会自动
 * 被评估为 true。 这曾是默认行为。 现在它已被弃用，
 * 因为它往往会给出无意义的答案，但某些应用程序
 * 可能仍然会有用处。
 */
#define TS_EXEC_SKIP_NOT		(0x01)
/*
 * 如果设置了 TS_EXEC_PHRASE_NO_POS，则在没有位置
 * 信息的情况下，允许 OP_PHRASE 以丢失的方式执行：
 * true 结果表示该短语可能存在。 如果没有此标志，
 * 如果没有词素位置信息，则 OP_PHRASE 始终返回 false。
 */
#define TS_EXEC_PHRASE_NO_POS	(0x02)

extern bool TS_execute(QueryItem *curitem, void *arg, uint32 flags,
					   TSExecuteCallback chkcond);
extern TSTernaryValue TS_execute_ternary(QueryItem *curitem, void *arg,
										 uint32 flags,
										 TSExecuteCallback chkcond);
extern bool tsquery_requires_match(QueryItem *curitem);

/*
 * to_ts* - 文本转换为 tsvector, tsquery
 */
extern TSVector make_tsvector(ParsedText *prs);
extern int32 tsCompareString(char *a, int lena, char *b, int lenb, bool prefix);

/*
 * 索引的可能策略编号
 *	  TSearchStrategyNumber  - (tsvector|text) @@ tsquery
 *	  TSearchWithClassStrategyNumber  - tsvector @@@ tsquery
 */
#define TSearchStrategyNumber			1
#define TSearchWithClassStrategyNumber	2

/*
 * TSQuery 工具
 */
extern QueryItem *clean_NOT(QueryItem *ptr, int32 *len);
extern TSQuery cleanup_tsquery_stopwords(TSQuery in);

typedef struct QTNode
{
	QueryItem  *valnode;
	uint32		flags;
	int32		nchild;
	char	   *word;
	uint32		sign;
	struct QTNode **child;
} QTNode;

/* QTNode.flags 中的位 */
#define QTN_NEEDFREE	0x01
#define QTN_NOCHANGE	0x02
#define QTN_WORDFREE	0x04

typedef uint64 TSQuerySign;

#define TSQS_SIGLEN  (sizeof(TSQuerySign)*BITS_PER_BYTE)

#define TSQuerySignGetDatum(X)		Int64GetDatum((int64) (X))
#define DatumGetTSQuerySign(X)		((TSQuerySign) DatumGetInt64(X))
#define PG_RETURN_TSQUERYSIGN(X)	return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n)	DatumGetTSQuerySign(PG_GETARG_DATUM(n))


extern QTNode *QT2QTN(QueryItem *in, char *operand);
extern TSQuery QTN2QT(QTNode *in);
extern void QTNFree(QTNode *in);
extern void QTNSort(QTNode *in);
extern void QTNTernary(QTNode *in);
extern void QTNBinary(QTNode *in);
extern int	QTNodeCompare(QTNode *an, QTNode *bn);
extern QTNode *QTNCopy(QTNode *in);
extern void QTNClearFlags(QTNode *in, uint32 flags);
extern bool QTNEq(QTNode *a, QTNode *b);
extern TSQuerySign makeTSQuerySign(TSQuery a);
extern QTNode *findsubquery(QTNode *root, QTNode *ex, QTNode *subs,
							bool *isfind);

#endif							/* _PG_TS_UTILS_H_ */
